package jehc.djshi.web.config;

import com.alibaba.druid.filter.stat.StatFilter;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import jehc.djshi.common.base.druid.DruidConfigBean;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
import java.sql.SQLException;

/**
 * @Desc 开启druid配置
 * @Author 邓纯杰
 * @CreateTime 2012-12-12 12:12:12
 */
@Slf4j
@Configuration
public class DruidConfig {

    @Autowired
    private DruidConfigBean druidConfigBean;

    /**
     * 主库
     * @return
     * @throws Exception
     */
    @Bean("dataSourceMaster")
    public DataSource dataSourceMaster() throws Exception {
        log.info("[信息]：主数据库加载.");
        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
        return this.druidDataSourceMaster(dataSource);
    }

    /**
     * 从库
     * @return
     * @throws Exception
     */
    @Bean("dataSourceSlave")
    public DataSource dataSourceSlave() throws Exception {
        log.info("[信息]：从库数据库加载.");
        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
        return this.druidDataSourceSlave(dataSource);
    }

    /**
     * 注册一个StatFilter
     * @return
     */
    @Bean
    @Primary
    public StatFilter statFilter() {
        StatFilter statFilter = new StatFilter();
        statFilter.setMergeSql(druidConfigBean.isMergeSql());
        statFilter.setLogSlowSql(druidConfigBean.isLogSlowSql());
        statFilter.setSlowSqlMillis(druidConfigBean.getLowSqlMillis());
        return statFilter;
    }

    /**
     * 注册一个StatViewServlet
     * @return
     */
    @Bean
    public ServletRegistrationBean druidStatViewServlet() {
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(),
                "/monitor/druid/*");
        servletRegistrationBean.addInitParameter("allow", druidConfigBean.getAllow());
        servletRegistrationBean.addInitParameter("deny", druidConfigBean.getDeny());
        servletRegistrationBean.addInitParameter("loginUsername", druidConfigBean.getLoginUsername());
        servletRegistrationBean.addInitParameter("loginPassword", druidConfigBean.getLoginPassword());
        servletRegistrationBean.addInitParameter("resetEnable", druidConfigBean.getResetEnable());
        return servletRegistrationBean;
    }

    /**
     * 注册一个：filterRegistrationBean
     * @return
     */
    @Bean
    public FilterRegistrationBean druidStatFilter() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
        filterRegistrationBean.addUrlPatterns("/*");
        filterRegistrationBean.addInitParameter("exclusions", druidConfigBean.getExclusions());
        return filterRegistrationBean;
    }

    /**
     * 定义主库数据源
     * @param dataSource
     * @return
     * @throws Exception
     */
    private DataSource druidDataSourceMaster(DruidDataSource dataSource) throws Exception {
        log.info("[信息]：初始化Druid的数据源");

        ////////////监控sql异常 排查问题需加上 生产环境去除 开始///////////
        dataSource.setRemoveAbandoned (true);//#超过时间限制是否回收
        dataSource.setRemoveAbandonedTimeout(180);//#超时时间；单位为秒。180秒=3分
        dataSource.setLogAbandoned(true);//关闭abanded连接时输出错误日志
        ////////////监控sql异常 排查问题需加上 生产环境去除 结束///////////

        dataSource.setUrl(druidConfigBean.getMasterUrl());
        dataSource.setUsername(druidConfigBean.getMasterName());
        dataSource.setPassword(druidConfigBean.getMasterPassword());
        dataSource.setDbType(druidConfigBean.getType());
        dataSource.setDriverClassName(druidConfigBean.getDriverClassName());
        /** 配置初始化大小、最小、最大 */
        dataSource.setInitialSize(druidConfigBean.getInitialSize());
        dataSource.setMaxActive(druidConfigBean.getMaxActive());
        dataSource.setMinIdle(druidConfigBean.getMinIdle());
        /** 配置获取连接等待超时的时间 */
        dataSource.setMaxWait(druidConfigBean.getMaxWait());
        /** 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位是毫秒 */
        dataSource.setTimeBetweenEvictionRunsMillis(druidConfigBean.getTimeBetweenEvictionRunsMillis());
        /** 配置一个连接在池中最小、最大生存的时间，单位是毫秒 */
        dataSource.setMinEvictableIdleTimeMillis(druidConfigBean.getMinEvictableIdleTimeMillis());
        dataSource.setMaxEvictableIdleTimeMillis(druidConfigBean.getMaxEvictableIdleTimeMillis());
        /**
         * 用来检测连接是否有效的sql，要求是一个查询语句，常用select
         * 'x'。如果validationQuery为null，testOnBorrow、testOnReturn、
         * testWhileIdle都不会起作用。
         */
        dataSource.setValidationQuery(druidConfigBean.getValidationQuery());
        /**
         * 建议配置为true，不影响性能，并且保证安全性。申请连接的时候检测，
         * 如果空闲时间大于timeBetweenEvictionRunsMillis，执行validationQuery检测连接是否有效。
         */
        dataSource.setTestWhileIdle(druidConfigBean.isTestWhileIdle());
        /** 申请连接时执行validationQuery检测连接是否有效，做了这个配置会降低性能。 */
        dataSource.setTestOnBorrow(druidConfigBean.isTestOnBorrow());
        /** 归还连接时执行validationQuery检测连接是否有效，做了这个配置会降低性能。 */
        dataSource.setTestOnReturn(druidConfigBean.isTestOnReturn());
        if (!StringUtils.isEmpty(druidConfigBean.getConnectionProperties())) {
            dataSource.setConnectionProperties(druidConfigBean.getConnectionProperties());
        }
        try {
            /** 配置监控统计拦截的filters */
            dataSource.setFilters(druidConfigBean.getFilters());
        } catch (SQLException e) {
            log.error("[信息]filters错误", e);

        }
        /** 打开PSCache，并且指定每个连接上PSCache的大小 */
        dataSource.setPoolPreparedStatements(druidConfigBean.isPoolPreparedStatements());
        dataSource.setMaxPoolPreparedStatementPerConnectionSize(druidConfigBean.getMaxPoolPreparedStatementPerConnectionSize());
        return dataSource;
    }


    /**
     * 定义从库数据源
     * @param dataSource
     * @return
     */
    public DataSource druidDataSourceSlave(DruidDataSource dataSource) {
        log.info("[信息]：初始化Druid的数据源");

        ////////////监控sql异常 排查问题需加上 生产环境去除 开始///////////
        dataSource.setRemoveAbandoned (true);//#超过时间限制是否回收
        dataSource.setRemoveAbandonedTimeout(180);//#超时时间；单位为秒。180秒=3分
        dataSource.setLogAbandoned(true);//关闭abanded连接时输出错误日志
        ////////////监控sql异常 排查问题需加上 生产环境去除 结束///////////


        dataSource.setUrl(druidConfigBean.getSlaveUrl());
        dataSource.setUsername(druidConfigBean.getSlaveName());
        dataSource.setPassword(druidConfigBean.getSlavePassword());
        dataSource.setDbType(druidConfigBean.getType());
        dataSource.setDriverClassName(druidConfigBean.getDriverClassName());
        /** 配置初始化大小、最小、最大 */
        dataSource.setInitialSize(druidConfigBean.getInitialSize());
        dataSource.setMaxActive(druidConfigBean.getMaxActive());
        dataSource.setMinIdle(druidConfigBean.getMinIdle());
        /** 配置获取连接等待超时的时间 */
        dataSource.setMaxWait(druidConfigBean.getMaxWait());
        /** 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位是毫秒 */
        dataSource.setTimeBetweenEvictionRunsMillis(druidConfigBean.getTimeBetweenEvictionRunsMillis());
        /** 配置一个连接在池中最小、最大生存的时间，单位是毫秒 */
        dataSource.setMinEvictableIdleTimeMillis(druidConfigBean.getMinEvictableIdleTimeMillis());
        dataSource.setMaxEvictableIdleTimeMillis(druidConfigBean.getMaxEvictableIdleTimeMillis());
        /**
         * 用来检测连接是否有效的sql，要求是一个查询语句，常用select
         * 'x'。如果validationQuery为null，testOnBorrow、testOnReturn、
         * testWhileIdle都不会起作用。
         */
        dataSource.setValidationQuery(druidConfigBean.getValidationQuery());
        /**
         * 建议配置为true，不影响性能，并且保证安全性。申请连接的时候检测，
         * 如果空闲时间大于timeBetweenEvictionRunsMillis，执行validationQuery检测连接是否有效。
         */
        dataSource.setTestWhileIdle(druidConfigBean.isTestWhileIdle());
        /** 申请连接时执行validationQuery检测连接是否有效，做了这个配置会降低性能。 */
        dataSource.setTestOnBorrow(druidConfigBean.isTestOnBorrow());
        /** 归还连接时执行validationQuery检测连接是否有效，做了这个配置会降低性能。 */
        dataSource.setTestOnReturn(druidConfigBean.isTestOnReturn());
        if (!StringUtils.isEmpty(druidConfigBean.getConnectionProperties())) {
            dataSource.setConnectionProperties(druidConfigBean.getConnectionProperties());
        }
        try {
            /** 配置监控统计拦截的filters */
            dataSource.setFilters(druidConfigBean.getFilters());
        } catch (SQLException e) {
            log.error("[信息]filters错误", e);

        }
        /** 打开PSCache，并且指定每个连接上PSCache的大小 */
        dataSource.setPoolPreparedStatements(druidConfigBean.isPoolPreparedStatements());
        dataSource.setMaxPoolPreparedStatementPerConnectionSize(druidConfigBean.getMaxPoolPreparedStatementPerConnectionSize());
        return dataSource;
    }
}
